#pragma once
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
#include <queue>

const int cap_default = 5;

template <typename T>
class BlockQueue
{
public:
    bool isEmpty()
    {
        return _bq.empty();
    }
    bool isFull()
    {
        return _bq.size() == _cap;
    }

public:
    BlockQueue(int cap = cap_default) : _cap(cap)
    {
        // 初始化锁和条件变量
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
    }
    void Equeue(T *in)
    {
        pthread_mutex_lock(&_lock);

        while (isFull())
        {
            // 满了，生产者不能生产，必须等待
            // 可是在临界区里面啊！！！pthread_cond_wait
            // 被调用的时候：除了让自己继续排队等待，还会自己释放传入的锁
            // 函数返回的时候，不就还在临界区了！
            // 返回时：必须先参与锁的竞争，重新加上锁，该函数才会返回！
            pthread_cond_wait(&_p_cond, &_lock);
        }
        // 队列没满/被唤醒了
        _bq.push(*in); ///// pthread_cond_broadcast : 一种场景
        pthread_mutex_unlock(&_lock);
        pthread_cond_broadcast(&_c_cond); // 一种场景
        // pthread_cond_signal(&_c_cond); // 生产出产品了,提醒消费者去消费
    }
    // 假设有两个消费者
    void Pop(T *out)
    {
        pthread_mutex_lock(&_lock);
        // while可以保证代码的鲁棒性(健壮性)
        // 为什么要用while,如果是多个线程同时等待,同时被唤醒,没用while判断的话会导致又有些不满足条件的线程访问临界资源
        while (isEmpty())
        {
            // 添加尚未满足，但是线程被异常唤醒的情况，叫做伪唤醒！
            // 队列为空,不能消费,需要等待生产者生产产品后将其唤醒
            pthread_cond_wait(&_c_cond, &_lock); // 两个消费者都在这里等待了
        }
        *out = _bq.front();
        _bq.pop();
        pthread_mutex_unlock(&_lock);
        // pthread_cond_broadcast(&_p_cond); 
        pthread_cond_signal(&_c_cond); // 消费后提醒生产者去生产,该语句放在解锁的前后都行
    }
    ~BlockQueue()
    {
        // 销毁锁和条件变量(同步)
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_p_cond);
        pthread_cond_destroy(&_c_cond);
    }

private:
    std::queue<T> _bq;
    int _cap;
    pthread_mutex_t _lock;  // 两种身份共用同一把锁才能保证临界区的安全
    pthread_cond_t _p_cond; // 生产者的条件变量
    pthread_cond_t _c_cond; // 消费者的条件变量
};